/*============================================================================

      unmove.cpp  -  Copyright (C) 1993-2005 by Don Cross

      Contains code for unmaking a move on a ChessBoard.
      When making a move in a ChessBoard, state information
      is saved in an 'UnmoveInfo' struct which allows this
      code to restore the previous state of the board.
      This is a lot faster than saving and restoring the entire board,
      though this is more complicated.

============================================================================*/

#include "chess.h"
#include "profiler.h"

#define DEBUG_UNMOVE 0

void ChessBoard::UnmakeWhiteMove ( Move move, UnmoveInfo &unmove )
{
    PROFILER_ENTER(PX_UNMOVE);

    int      source = move.source & BOARD_OFFSET_MASK;
    int      dest;
    SQUARE   capture = unmove.capture;
    int      prom_piece_index;

#if DEBUG_UNMOVE
    if ( board[source] != EMPTY )
        ChessFatal ( "source not empty in UnmakeWhiteMove" );

    SQUARE destSquare = EMPTY;
#endif

    if ( move.dest > OFFSET(9,9) )
    {
        // This is a "special" move...

        switch ( move.dest & 0xF0 )
        {
            case SPECIAL_MOVE_PROMOTE_NORM:
                prom_piece_index = (move.dest & PIECE_MASK) | WHITE_IND;
                dest = source + NORTH;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = WPAWN;
                board [dest] = EMPTY;
                ++inventory [WP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_PROMOTE_CAP_EAST:
                prom_piece_index = (move.dest & PIECE_MASK) | WHITE_IND;
                dest = source + NORTHEAST;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = WPAWN;
                board [dest] = capture;
                ++inventory [WP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_PROMOTE_CAP_WEST:
                prom_piece_index = (move.dest & PIECE_MASK) | WHITE_IND;
                dest = source + NORTHWEST;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = WPAWN;
                board [dest] = capture;
                ++inventory [WP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_KCASTLE:
                #if DEBUG_UNMOVE
                    destSquare = board[OFFSET(8,2)];
                #endif
                wk_offset = OFFSET(6,2);
                board [ OFFSET(6,2) ] = WKING;
                board [ OFFSET(9,2) ] = WROOK;
                board [ OFFSET(7,2) ] = board [ OFFSET(8,2) ] = EMPTY;
                break;

            case SPECIAL_MOVE_QCASTLE:
                #if DEBUG_UNMOVE
                    destSquare = board[OFFSET(4,2)];
                #endif
                wk_offset = OFFSET(6,2);
                board [ OFFSET(6,2) ] = WKING;
                board [ OFFSET(2,2) ] = WROOK;
                board [ OFFSET(4,2) ] = board [ OFFSET(5,2) ] = EMPTY;
                break;

            case SPECIAL_MOVE_EP_EAST:
                board [source] = board [dest = source + NORTHEAST];
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [dest] = EMPTY;
                board [source + EAST] = capture;
                break;

            case SPECIAL_MOVE_EP_WEST:
                board [source] = board [dest = source + NORTHWEST];
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [dest] = EMPTY;
                board [source + WEST] = capture;
                break;

            default:
                ChessFatal ( "Invalid special move code in ChessBoard::UnmakeWhiteMove" );
                break;
        }
    }
    else
    {
        // This is a "normal" move...
        dest = move.dest;
        SQUARE move_piece = board[source] = board[dest];
        #if DEBUG_UNMOVE
            destSquare = board[dest];
        #endif
        board[dest] = capture;

        if ( move_piece & WK_MASK )
            wk_offset = source;
    }

#if DEBUG_UNMOVE
    if ( !(destSquare & WHITE_MASK) )
        ChessFatal ( "Attempt to unmove non-white piece in UnmakeWhiteMove" );
#endif

    if ( capture != EMPTY )
        ++inventory [SPIECE_INDEX(capture)];

    flags            =  unmove.flags;
    bmaterial        =  unmove.bmaterial;
    wmaterial        =  unmove.wmaterial;
    prev_move        =  unmove.prev_move;
    lastCapOrPawn    =  unmove.lastCapOrPawn;
    repeatHash [cachedHash % REPEAT_HASH_SIZE] -= 0x10;  // no longer Black to move
    cachedHash       =  unmove.cachedHash;

    --ply_number;
    white_to_move = true;

    PROFILER_EXIT();
}


//------------------------------------------------------------------------


void ChessBoard::UnmakeBlackMove ( Move move, UnmoveInfo &unmove )
{
    PROFILER_ENTER(PX_UNMOVE)

    int      source = move.source & BOARD_OFFSET_MASK;
    int      dest;
    SQUARE   capture = unmove.capture;
    SQUARE   prom_piece_index;

#if DEBUG_UNMOVE
    if ( board[source] != EMPTY )
        ChessFatal ( "source not empty in UnmakeWhiteMove" );

    SQUARE destSquare = EMPTY;
#endif

    if ( move.dest > OFFSET(9,9) )
    {
        // This is a "special" move...

        switch ( move.dest & 0xF0 )
        {
            case SPECIAL_MOVE_PROMOTE_NORM:
                prom_piece_index = (move.dest & PIECE_MASK) | BLACK_IND;
                dest = source + SOUTH;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = BPAWN;
                board [dest] = EMPTY;
                ++inventory [BP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_PROMOTE_CAP_EAST:
                prom_piece_index = (move.dest & PIECE_MASK) | BLACK_IND;
                dest = source + SOUTHEAST;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = BPAWN;
                board [dest] = capture;
                ++inventory [BP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_PROMOTE_CAP_WEST:
                prom_piece_index = (move.dest & PIECE_MASK) | BLACK_IND;
                dest = source + SOUTHWEST;
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [source] = BPAWN;
                board [dest] = capture;
                ++inventory [BP_INDEX];
                --inventory [prom_piece_index];
                break;

            case SPECIAL_MOVE_KCASTLE:
                #if DEBUG_UNMOVE
                    destSquare = board[OFFSET(8,9)];
                #endif
                bk_offset = OFFSET(6,9);
                board [ OFFSET(6,9) ] = BKING;
                board [ OFFSET(9,9) ] = BROOK;
                board [ OFFSET(7,9) ] = board [ OFFSET(8,9) ] = EMPTY;
                break;

            case SPECIAL_MOVE_QCASTLE:
                #if DEBUG_UNMOVE
                    destSquare = board[OFFSET(4,9)];
                #endif
                bk_offset = OFFSET(6,9);
                board [ OFFSET(6,9) ] = BKING;
                board [ OFFSET(2,9) ] = BROOK;
                board [ OFFSET(4,9) ] = board [ OFFSET(5,9) ] = EMPTY;
                break;

            case SPECIAL_MOVE_EP_EAST:
                board [source] = board [dest = source + SOUTHEAST];
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [dest] = EMPTY;
                board [source + EAST] = capture;
                break;

            case SPECIAL_MOVE_EP_WEST:
                board [source] = board [dest = source + SOUTHWEST];
                #if DEBUG_UNMOVE
                    destSquare = board[dest];
                #endif
                board [dest] = EMPTY;
                board [source + WEST] = capture;
                break;

            default:
                ChessFatal ( "Invalid special move code in ChessBoard::UnmakeBlackMove" );
                break;
        }
    }
    else
    {
        // This is a "normal" move...
        dest = move.dest;
        #if DEBUG_UNMOVE
            destSquare = board[dest];
        #endif
        SQUARE move_piece = board[source] = board[dest];
        board [dest] = capture;

        if ( move_piece & BK_MASK )
            bk_offset = source;
    }

#if DEBUG_UNMOVE
    if ( !(destSquare & BLACK_MASK) )
        ChessFatal ( "Attempt to unmove non-black piece in UnmakeBlackMove" );
#endif

    if ( capture != EMPTY )
        ++inventory [SPIECE_INDEX(capture)];

    flags            =  unmove.flags;
    bmaterial        =  unmove.bmaterial;
    wmaterial        =  unmove.wmaterial;
    prev_move        =  unmove.prev_move;
    lastCapOrPawn    =  unmove.lastCapOrPawn;

    repeatHash [cachedHash % REPEAT_HASH_SIZE] -= 0x01;  // no longer White to move
    cachedHash       =  unmove.cachedHash;

    --ply_number;
    white_to_move = false;

    PROFILER_EXIT()
}
